package com.jinyang.jetpackdemo.activity

import android.graphics.drawable.Drawable
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.bumptech.glide.Glide
import com.jinyang.jetpackdemo.R
import com.jinyang.jetpackdemo.util.gson.GsonUtils
import com.jinyang.jetpackdemo.util.LjyLogUtil
import kotlinx.android.synthetic.main.activity_coroutine.*
import kotlinx.android.synthetic.main.activity_ok_http_demo.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
import kotlinx.coroutines.selects.select
import okhttp3.Call
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.io.IOException
import java.util.concurrent.Executors
import kotlin.coroutines.*

class CoroutineActivity : AppCompatActivity() {
    private var startTime: Long = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_coroutine)
        startTime = System.currentTimeMillis()
        //1.减少回调地狱
        btn_1.setOnClickListener {
            test1()
        }
        //2.合并请求结果
        btn_2.setOnClickListener {
            test2()
        }
        //3.协程上下文
        btn_3.setOnClickListener {
            test3()
        }
        //4.协程拦截器
        btn_4.setOnClickListener {
            test4()
        }
        //5.协程调度器
        btn_5.setOnClickListener {
            test5()
        }
        //6.其实在多个线程上运行协程，线程总是这样切来切去其实并不会显得很轻量级，例如下面的例子就是比较可怕的了：
        //这里面除了 delay 那里有一次不可避免的线程切换外，其他几处协程挂起点的继续操作（Continuation.resume）都会切线程
        btn_6.setOnClickListener {
            test6()
        }
        //7.线程安全问题
        btn_7.setOnClickListener {
            test7()
        }
        //8.suspendCoroutine：使用协程取代回调函数
        btn_8.setOnClickListener {
            test8()
            test82()
        }
        //9.全局异常处理:CoroutineExceptionHandler
        btn_9.setOnClickListener {
            test9()
        }
        //10.协程的取消
        btn_10.setOnClickListener {
            test10()
        }
        //11.完善之前的例子
        btn_11.setOnClickListener {
            test11()
        }
        //12.suspendCancellableCoroutine:让挂起函数支持协程的取消
        //该方法将获取到的 Continuation 包装成了一个 CancellableContinuation，
        // 通过调用它的 invokeOnCancellation 方法可以设置一个取消事件的回调，
        // 一旦这个回调被调用，那么意味着 getUserCoroutine 调用所在的协程被取消了，
        // 这时候我们也要相应的做出取消的响应，也就是把 OkHttp 发出去的请求给取消掉。
        btn_12.setOnClickListener {
            test12()
        }
        //13.Channel:Channel 实际上就是一个队列，而且是并发安全的，它可以用来连接协程，实现不同协程的通信
        //把过去你用 BlockingQueue 实现的功能替换成 Channel
        btn_13.setOnClickListener {
            test13()
        }
        //14.BroadcastChannel:从数据处理的本身来讲，虽然有多个接收端，同一个元素只会被一个接收端读到。广播则不然，多个接收端不存在互斥行为。
        btn_14.setOnClickListener {
            test14()
        }
        //15.协程 Select 多路复用
        //如果我有这样一个场景，两个 API 分别从网络和本地缓存获取数据，期望哪个先返回就先用哪个做展示：
        btn_15.setOnClickListener {
            test15()
        }
        //16.复用多个 Channel
        btn_16.setOnClickListener {
            test16()
        }
        //17.SelectClause: 怎么知道哪些事件可以被 select 呢？其实所有能够被 select 的事件都是 SelectClauseN 类型
        btn_17.setOnClickListener {
            test17()
        }
        //18.flow: 就是 Kotlin 协程与响应式编程模型结合的产物，你会发现它与 RxJava 非常像，二者之间也有相互转换的 API，使用起来非常方便。
        btn_18.setOnClickListener {
            test18()
        }
        //19.flow的背压
        btn_19.setOnClickListener {
            test19()
        }
        //20.Flow 的变换
        btn_20.setOnClickListener {
            test20()
        }
        //21.使用 Flow 实现多路复用
        btn_21.setOnClickListener {
            test21()
        }
        //22.使用 Flow 实现对 Channel 的复用
        btn_22.setOnClickListener {
            test22()
        }

        //23.使用 Flow 各种操作符
        btn_23.setOnClickListener {
            test23()
        }

        //24.使用 SharedFlow
        btn_24.setOnClickListener {
            test24()
        }
        //25.使用 StateFlow
        btn_25.setOnClickListener {
            test25()
        }
    }

    private fun test25() {
        log("StateFlow 默认值:111")
        val stateFlow = MutableStateFlow("111")

        lifecycleScope.launch {
            delay(500)
            stateFlow.collect { log("StateFlow collect1:$it") }
        }
        lifecycleScope.launch {
            delay(1500)
            stateFlow.collect { log("StateFlow collect2:$it") }
        }
        lifecycleScope.launch {
            delay(2500)
            stateFlow.collect { log("StateFlow collect3:$it") }
        }

        lifecycleScope.launch(Dispatchers.IO) {
            delay(5000)
            log("StateFlow re emit:111")
            stateFlow.emit("111")
            delay(1000)
            log("StateFlow emit:222")
            stateFlow.emit("222")
        }

        //Flow转化成StateFlow
        val stateFlow2: StateFlow<Int> = flow {
            List(10) {
                delay(300)
                emit(it)
            }
        }.stateIn(
            scope = lifecycleScope,
            started = WhileSubscribed(5000),//等待5秒后仍然没有订阅者存在就终止协程
            initialValue = 666//默认值
        )
        lifecycleScope.launchWhenStarted {//STARTED状态时会开始收集流，并且在RESUMED状态时保持收集，进入STOPPED状态时结束收集过程
            stateFlow2.collect { log("StateFlow shareIn.collect:$it") }
        }
    }

    private fun test24() {
        val sharedFlow = MutableSharedFlow<String>()
        lifecycleScope.launch(Dispatchers.IO) {
            delay(1000)
            sharedFlow.emit("aaa")
            delay(1000)
            sharedFlow.emit("bbb")
            delay(1000)
            sharedFlow.emit("ccc")
        }
        lifecycleScope.launch {
            delay(500)
            sharedFlow.collect { log("collect1:$it") }
        }
        lifecycleScope.launch {
            delay(1500)
            sharedFlow.collect { log("collect2:$it") }
        }
        lifecycleScope.launch {
            delay(2500)
            sharedFlow.collect { log("collect3:$it") }
        }
        lifecycleScope.launch {
            delay(3500)
            sharedFlow.collect { log("collect4:$it") }
        }

        lifecycleScope.launch {
            (1..5).asFlow().shareIn(
                //1. 共享开始时所在的协程作用域范围
                scope = lifecycleScope,
                //2. 控制共享的开始和结束的策略
                // started = SharingStarted.Lazily,//当首个订阅者出现时开始，在scope指定的作用域被结束时终止
                // started = SharingStarted.Eagerly,//立即开始，而在scope指定的作用域被结束时终止
                //对于那些只执行一次的操作，您可以使用Lazily或者Eagerly。然而，如果您需要观察其他的流，就应该使用WhileSubscribed来实现细微但又重要的优化工作
                //WhileSubscribed策略会在没有收集器的情况下取消上游数据流
                started = SharingStarted.WhileSubscribed(
                    500,//stopTimeoutMillis 控制一个以毫秒为单位的延迟值，指的是最后一个订阅者结束订阅与停止上游流的时间差。默认值是 0(比如当用户旋转设备时，原来的视图会先被销毁，然后数秒钟内重建)
                    Long.MAX_VALUE//replayExpirationMillis表示数据重播的过时时间，如果用户离开应用太久，此时您不想让用户看到陈旧的数据，你可以用到这个参数
                ),
                //3. 状态流的重播个数
                replay = 0
            ).collect { log("shareIn.collect:$it") }

        }

    }

    private fun test23() {
//        val flow = flowOf(1,2,3,4,5)
//        val flow: Flow<Int> = flow {
//            List(20) {
//                emit(it)//发送数据
//                delay(300)
//            }
//        }
        val flow = (1..10).asFlow()
        lifecycleScope.launch {
            flow.flowOn(Dispatchers.IO)//设定它运行时所使用的调度器,设置的调度器只对它之前的操作有影响
                .onStart { log("onStart") }
                .flowOn(Dispatchers.Main)
                .onEach {
                    log("onEach:$it")
                    delay(300)
                }
                .filter {//过滤
                    it % 2 == 0
                }
                .map {//变换
                    log("map:$it*$it")
                    it * it
                }
                .transform<Int,String> {
                    "num=$it"
//                    emit("num1=$it")
//                    emit("num2=$it")
                }
                .flowOn(Dispatchers.IO)
                .onCompletion {//订阅流的完成,执行在流完成时的逻辑
                    log("onCompletion: $it")
                }
                .catch {//捕获 Flow 的异常,catch 函数只能捕获它的上游的异常
                    log("catch: $it")
                }
                .flowOn(Dispatchers.Main)
                .collect {//消费Flow
                    log("collect1_1: $it")
                }
            //Flow 可以被重复消费
            flow.collect { log("collect1_2: $it") }
            //除了可以在 collect 处消费 Flow 的元素以外，还可以通过 onEach 来做到这一点。
            // 这样消费的具体操作就不需要与末端操作符放到一起，collect 函数可以放到其他任意位置调用
            flow.onEach {
                log("onEach2:$it")
            }
            withContext(Dispatchers.IO) {
                delay(1000)
                flow.collect()
            }
            //除了使用子协程执行上流外，我们还可以使用launchIn函数来让Flow使用全新的协程上下文
            flow.onEach {
                log("onEach2:$it")
            }.launchIn(CoroutineScope(Dispatchers.IO))
                .join()//主线程等待这个协程执行结束

        }
        //flow的取消
        lifecycleScope.launch(Dispatchers.IO) {
            val flow2 = (1..10).asFlow().onEach { delay(1000) }
            val job: Job = lifecycleScope.launch {
                log("lifecycleScope.launch")
                flow2.flowOn(Dispatchers.IO)//设定它运行时所使用的调度器
                    .collect {//消费Flow
                        log("flow2:$it")
                    }
            }
            delay(2000)
            job.cancelAndJoin()
        }
        lifecycleScope.launch {
            //Flow 的背压
            //只要是响应式编程，就一定会有背压问题，我们先来看看背压究竟是什么。
            //背压问题在生产者的生产速率高于消费者的处理速率的情况下出现。为了保证数据不丢失，我们也会考虑添加缓存来缓解问题：
            //为 Flow 添加缓冲
            flow {
                List(5) {
                    emit(it)
                }
            }.buffer().collect {
                log("flow buffer collect:$it")
            }
            //也可以为 buffer 指定一个容量。不过，如果我们只是单纯地添加缓存，而不是从根本上解决问题就始终会造成数据积压。
            //问题产生的根本原因是生产和消费速率的不匹配，除直接优化消费者的性能以外，我们也可以采取一些取舍的手段。
            //第一种是 conflate。与 Channel 的 Conflate 模式一致，新数据会覆盖老数据，
            flow {
                List(10) {
                    emit(it)
                }
            }.conflate()
                .collect { value ->
                    log("flow conflate Collecting $value")
                    delay(100)
                    log("$value collected flow conflate ")
                }
            // 第二种是 collectLatest。顾名思义，只处理最新的数据，这看上去似乎与 conflate 没有区别，
            // 其实区别大了：它并不会直接用新数据覆盖老数据，而是每一个都会被处理，
            // 只不过如果前一个还没被处理完后一个就来了的话，处理前一个数据的逻辑就会被取消
            //除 collectLatest 之外还有 mapLatest、flatMapLatest 等等，都是这个作用。
            flow {
                List(10) {
                    emit(it)
                }
            }.collectLatest { value ->
                log("flow collectLatest Collecting $value")
                delay(100)
                log("$value collected flow collectLatest ")
            }
        }
        lifecycleScope.launch {
            delay(500)
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                flow.collect { log("collect2: $it") }
            }
        }
        lifecycleScope.launchWhenStarted {
            delay(1000)
            flow.collect { log("collect3: $it") }
        }
        lifecycleScope.launch {
            delay(1500)
            flow.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
                .collect { log("collect4: $it") }
        }

    }

    private fun test22() {
        lifecycleScope.launch {
            val channels = List(10) { Channel<Int>() }
            val result = channels.map {
                it.consumeAsFlow()
            }
                .merge()
                .first()
            log(result)
        }
    }

    private fun test21() {
        lifecycleScope.launch {
            listOf(getUserFromNetAsync(), getUserFromLocalAsync())
                .map { deferred ->
                    flow { emit(deferred.await()) }
                }
                .merge()
                .onEach { user ->
                    println("Result: $user")
                }.collect {
                    log("collect:$it")
                }
        }

    }

    private fun test20() {
        lifecycleScope.launch {
            //map:Flow 的元素变换
            flow {
                List(5) { emit(it) }
            }
                .onEach { log("onEach:$it") }
                .map {
                    it * 2
                }
                .onEach { log("onEach:$it") }.collect {
                    log("collect:$it")
                }
            //Flow 的嵌套
            flow {
                List(5) { emit(it) }
            }.map {
                flow { List(it) { emit(it) } }
            }.onEach { log("onEach:$it") }.collect {
                log("collect:$it")
            }
            //拼接 Flow: flattenConcat
            flow {
                List(5) { emit(it) }
            }.map {
                flow { List(it) { emit(it) } }
            }.flattenConcat()
                .onEach { log("onEach:$it") }.collect {
                    log("collect:$it")
                }
        }
    }

    private fun test19() {
        lifecycleScope.launch {
            //Flow 的背压
            //只要是响应式编程，就一定会有背压问题，我们先来看看背压究竟是什么。
            //背压问题在生产者的生产速率高于消费者的处理速率的情况下出现。为了保证数据不丢失，我们也会考虑添加缓存来缓解问题：
            //为 Flow 添加缓冲
            flow {
                List(5) {
                    emit(it)
                }
            }.buffer().collect {
                log("flow buffer collect:$it")
            }
            //也可以为 buffer 指定一个容量。不过，如果我们只是单纯地添加缓存，而不是从根本上解决问题就始终会造成数据积压。
            //问题产生的根本原因是生产和消费速率的不匹配，除直接优化消费者的性能以外，我们也可以采取一些取舍的手段。
            //第一种是 conflate。与 Channel 的 Conflate 模式一致，新数据会覆盖老数据，
            flow {
                List(10) {
                    emit(it)
                }
            }.conflate()
                .collect { value ->
                    log("flow conflate Collecting $value")
                    delay(100)
                    log("$value collected flow conflate ")
                }
            // 第二种是 collectLatest。顾名思义，只处理最新的数据，这看上去似乎与 conflate 没有区别，
            // 其实区别大了：它并不会直接用新数据覆盖老数据，而是每一个都会被处理，
            // 只不过如果前一个还没被处理完后一个就来了的话，处理前一个数据的逻辑就会被取消
            //除 collectLatest 之外还有 mapLatest、flatMapLatest 等等，都是这个作用。
            flow {
                List(10) {
                    emit(it)
                }
            }.collectLatest { value ->
                log("flow collectLatest Collecting $value")
                delay(100)
                log("$value collected flow collectLatest ")
            }
        }
    }

    private fun test18() {

        val ints = sequence {
            (1..3).forEach {
                yield(it)
//                delay(1000) // ERROR!
            }
        }
        ints.forEach {
            log("ints:$it")
        }
        //序列生成器中不能调用其他挂起函数,通过flow可以实现
        //创建 Flow
        val intFlow: Flow<Int> = flow {
            (1..3).forEach {
                delay(1000)
                log("emit:$it")
                emit(it)
            }
        }.onEach { log("onEach:$it") }
            .onCompletion {
                log("onCompletion:$it")
            }
        lifecycleScope.launch {
            intFlow.flowOn(Dispatchers.IO)//设定它运行时所使用的调度器
                .collect {//消费Flow
                    log("intFlow:$it")
                }
            //冷数据流: 一个 Flow 创建出来之后，不消费则不生产，多次消费则多次生产，生产和消费总是相对应的。
            //所谓冷数据流，就是只有消费时才会生产的数据流，这一点与 Channel 正对应：Channel 的发送端并不依赖于接收端。
            //Flow 可以被重复消费
            intFlow.collect { log("intFlow2:$it") }
            intFlow.collect { log("intFlow3:$it") }
            //除了可以在 collect 处消费 Flow 的元素以外，还可以通过 onEach 来做到这一点。
            // 这样消费的具体操作就不需要与末端操作符放到一起，collect 函数可以放到其他任意位置调用
            intFlow.collect()
            //使用协程作用域直接触发 Flow
        }
        val intFlow2: Flow<Int> = flow {
            (4..6).forEach {
                delay(1000)
                log("emit:$it")
                emit(it)
            }
        }
            .onEach { log("onEach:$it") }

        intFlow2.launchIn(lifecycleScope)

        //flow的取消
        lifecycleScope.launch(Dispatchers.IO) {
            val job: Job = lifecycleScope.launch {
                log("lifecycleScope.launch")
                intFlow.flowOn(Dispatchers.IO)//设定它运行时所使用的调度器
                    .collect {//消费Flow
                        log("intFlow:$it")
                    }
            }
            delay(2000)
            job.cancelAndJoin()
        }
        //不能在 Flow 中直接切换调度器, 因为 emit 函数不是线程安全的：
        //想要在生成元素时切换调度器，就必须使用 channelFlow 函数来创建 Flow：
        lifecycleScope.launch {
            channelFlow {
                log("send 1")
                send(1)
                withContext(Dispatchers.IO) {
                    log("send 2")
                    send(2)
                }
            }.collect {
                log("channelFlow collect:$it")
            }
            //此外，我们也可以通过集合框架来创建 Flow：
            listOf(1, 2, 3, 4).asFlow().collect {
                log("listOf collect:$it")
            }
            setOf(1, 2, 3, 4).asFlow().collect {
                log("setOf collect:$it")
            }
            flowOf(1, 2, 3, 4).collect {
                log("flowOf collect:$it")
            }

        }


    }

    private fun test17() {
        val channels = List(10) { Channel<Int>() }
        lifecycleScope.launch {
            var count = 0
            channels.forEach { channel ->
                channel.send(count)
            }
        }
        lifecycleScope.launch {
            List(100) { element ->
                val num = select<Unit> {
                    channels.forEach { channel ->
                        channel.onSend(element) { sentChannel -> log("sent on $sentChannel") }
                    }
                }
                log("num=$num")
            }
        }
    }

    private fun test16() {
        val channels = List(10) { Channel<Int>() }
        lifecycleScope.launch {
            var count = 0
            channels.forEach { channel ->
                channel.send(count)
            }
        }
        lifecycleScope.launch {
            val num = select<Int?> {
                channels.forEach { channel ->
                    channel.onReceive {
                        it
                    }
                    // OR
//                    channel.onReceiveOrNull { it }
                }
            }
            log("num=$num")
        }
    }

    private fun test15() {
        lifecycleScope.launch {
            val localDeferred = getUserFromLocalAsync()
            val remoteDeferred = getUserFromNetAsync()

            val userResponse = select<User> {
                localDeferred.onAwait {
                    it
                }
                remoteDeferred.onAwait {
                    it
                }
            }
            log("userResponse.name=${userResponse.name}")
        }
    }

    private fun CoroutineScope.getUserFromNetAsync() = async {
        val delayTime = (Math.random() * 500).toLong()
        delay(delayTime)
        log("getUserFromNetAsync delayTime=$delayTime")
        User("LJY_Net", "", "", "")
    }

    private fun CoroutineScope.getUserFromLocalAsync() = async {
        val delayTime = (Math.random() * 500).toLong()
        delay(delayTime)
        log("getUserFromLocalAsync delayTime=$delayTime")
        User("LJY_Local", "", "", "")
    }

    private fun test14() {
        val broadcastChannel = BroadcastChannel<Int>(5)

        val producer = lifecycleScope.launch {
            var i = 0
            while (true) {
                broadcastChannel.send(i++)
                delay(1000)
            }
        }

        val consumer = lifecycleScope.launch {
            while (true) {
                val receiveChannel = broadcastChannel.openSubscription()
                for (element in receiveChannel) {
                    log("consumer1:$element")
                    delay(1000)
                    if (element > 20) {
                        broadcastChannel.cancel()
                    }
                }
            }
        }
        val consumer2 = lifecycleScope.launch {
            while (true) {
                val receiveChannel = broadcastChannel.openSubscription()
                for (element in receiveChannel) {
                    log("consumer2:$element")
                    delay(1000)
                    if (element > 20) {
                        broadcastChannel.close()
                    }
                }
            }
        }

//        producer.join()
//        consumer.join()
    }

    private fun test13() {
        val channel = Channel<Int>()

        val producer = lifecycleScope.launch {
            var i = 0
            while (true) {
                channel.send(i++)
                delay(1000)
            }
        }

        val consumer = lifecycleScope.launch {
            while (true) {
                val element = channel.receive()
                log("consumer1:$element")
            }
        }
        val consumer2 = lifecycleScope.launch {
            while (true) {
                val element = channel.receive()
                log("consumer2:$element")
                if (element > 20) {
                    channel.close()
                }
            }
        }

//        producer.join()
//        consumer.join()
    }

    private fun test12() {
        val job1 = lifecycleScope.launch { //①
            log(1)
            val user = getUserCoroutine2()
            log(user)
            log(2)
        }
        log(3)
        job1.cancel()
        log(4)
    }

    private suspend fun getUserCoroutine2() = suspendCancellableCoroutine<User> { continuation ->
        val call = OkHttpClient().newCall(
            Request.Builder()
                .get().url("https://api.github.com/users/JinYangLiu")
                .build()
        )

        continuation.invokeOnCancellation { // ①
            log("invokeOnCancellation: cancel the request.")
            call.cancel()
        }

        call.enqueue(object : okhttp3.Callback {
            override fun onFailure(call: Call, e: IOException) {
                log("onFailure: $e")
                continuation.resumeWithException(e)
            }

            override fun onResponse(call: Call, response: Response) {
                log("onResponse: ${response.code()}")
                response.body()?.let {
                    try {
                        continuation.resume(GsonUtils.fromJson(it.string(), User::class.java)!!)
                    } catch (e: Exception) {
                        continuation.resumeWithException(e)
                    }
                } ?: continuation.resumeWithException(NullPointerException("ResponseBody is null."))
            }
        })
    }

    private fun test11() {
        lifecycleScope.launch {
            try {
                tv_info.text = GsonUtils.toJson(getUserCoroutine())
            } catch (e: Exception) {
                tv_info.text = "Get User Error: $e"
            }
        }
    }

    private suspend fun getUserCoroutine() = suspendCoroutine<User> { continuation ->
        getUser2(object : Callback<User> {
            override fun onSuccess(value: User) {
                continuation.resume(value)
            }

            override fun onError(t: Throwable) {
                continuation.resumeWithException(t)
            }
        })
    }

    data class User(val name: String, val bio: String, val avatar_url: String, val url: String)

    private fun getUser2(callback: Callback<User>) {
        val call = OkHttpClient().newCall(
            Request.Builder()
                .get().url("https://api.github.com/users/JinYangLiu")
                .build()
        )

        call.enqueue(object : okhttp3.Callback {
            override fun onFailure(call: Call, e: IOException) {
                callback.onError(e)
            }

            override fun onResponse(call: Call, response: Response) {
                response.body()?.let {
                    try {
                        callback.onSuccess(GsonUtils.fromJson(it.string(), User::class.java)!!)
                    } catch (e: Exception) {
                        callback.onError(e) // 这里可能是解析异常
                    }
                } ?: callback.onError(NullPointerException("ResponseBody is null."))
            }
        })
    }


    private fun test10() {
        runBlocking {
            val job1 = launch {
                log(1)
                //1
//                delay(1000)
                //2
                try {
                    delay(1000)
                } catch (e: Exception) {
                    log("cancelled. $e")
                }
                log(2)
            }
            delay(100)
            log(3)
            job1.cancel()
            log(4)
        }
    }


    private fun test9() {
        //1. java线程：
        //Thread.setDefaultUncaughtExceptionHandler {t: Thread, e: Throwable ->
        //        //handle exception here
        //        println("Thread '${t.name}' throws an exception with message '${e.message}'")
        //    }
        //
        //    throw ArithmeticException("Hey!")

        //2. rxJava
        //RxJavaPlugins.setErrorHandler(e -> {
        //        //handle exception here
        //        println("Throws an exception with message '${e.message}'")
        //});

        //3. 协程：
        val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
            log("Throws an exception with message: ${throwable.message}")
        }
        log(1)
        lifecycleScope.launch(exceptionHandler) {
            throw ArithmeticException("Hey!")
        }
        log(2)

    }

    private fun test82() {
        lifecycleScope.launch {
            try {
                tv_info.text = getData2().name
            } catch (e: Exception) {
                tv_info.text = "Get User Error: $e"
            }
        }
    }

    private suspend fun getData2() = suspendCoroutine<UserInfo> { continuation ->
        getUser(object : Callback<UserInfo> {
            override fun onSuccess(value: UserInfo) {
                continuation.resume(value)
            }

            override fun onError(t: Throwable) {
                continuation.resumeWithException(t)
            }

        })
    }

    private fun getUser(callback: Callback<UserInfo>) {
        Thread.sleep(1000)
        if ((Math.random() * 10).toInt() % 2 == 0) {
            callback.onSuccess(UserInfo(111, "LJY", "url111", "ddddd"))
        } else {
            callback.onError(IllegalStateException("获取数据失败"))
        }
    }

    interface Callback<T> {
        fun onSuccess(value: T)

        fun onError(t: Throwable)
    }

    private fun test8() {
        lifecycleScope.launch {
            try {
                tv_info.text = getData().name
            } catch (e: Exception) {
                tv_info.text = "Get User Error: $e"
            }
        }

    }

    private suspend fun getData() = suspendCoroutine<UserInfo> { continuation ->
        getUser {
            if (it == null) {
                continuation.resumeWithException(IllegalStateException("获取数据失败"))
            } else {
                continuation.resume(it)
            }
        }
    }

    private fun getUser(callback: (UserInfo?) -> Unit) {
        Thread.sleep(1000)
        if ((Math.random() * 10).toInt() % 2 == 0) {
            callback(UserInfo(111, "LJY", "url111", "ddddd"))
        } else {
            callback(null)
        }
    }

    private fun test7() {
        var i = 0
        lifecycleScope.launch {
            Executors.newFixedThreadPool(10)
                .asCoroutineDispatcher().use { dispatcher ->
                    List(10000) {
                        withContext(dispatcher) {
                            i++
                        }
                    }.forEach {
                        it
                    }
                }
            log(i)
        }
    }


    private fun test6() {
        lifecycleScope.launch {
            log("0")
            Executors.newFixedThreadPool(10)
                .asCoroutineDispatcher().use { dispatcher ->
                    withContext(dispatcher) {
                        log("1")
                        val job = async {
                            log("2.1")
                            delay(1000)
                            log("2.2")
                            "hello"
                        }
                        log("3")
                        val result = job.await()
                        log("4")
                        log("result:$result")
                    }
                    log("5")
                }
            log("6")
        }
    }

    private fun test5() {
        lifecycleScope.launch(Dispatchers.Main) {//Main: UI 线程
            LjyLogUtil.d("Dispatchers.Main: currentJob=${Job.currentJob()}, currentThread=${Thread.currentThread()}")
        }
        lifecycleScope.launch(Dispatchers.IO) {//IO: 线程池
            LjyLogUtil.d("Dispatchers.IO: currentJob=${Job.currentJob()}, currentThread=${Thread.currentThread()}")
        }
        lifecycleScope.launch(Dispatchers.Default) {//Default: 线程池
            LjyLogUtil.d("Dispatchers.Default: currentJob=${Job.currentJob()}, currentThread=${Thread.currentThread()}")
        }
        lifecycleScope.launch(Dispatchers.Unconfined) {//Unconfined: 直接执行
            LjyLogUtil.d("Dispatchers.Unconfined: currentJob=${Job.currentJob()}, currentThread=${Thread.currentThread()}")
        }
        //绑定到任意线程的调度器
        val myDispatcher =
            Executors.newSingleThreadExecutor { r -> Thread(r, "MyThread") }.asCoroutineDispatcher()
        lifecycleScope.launch(myDispatcher) {//Unconfined: 直接执行
            LjyLogUtil.d("myDispatcher: currentJob=${Job.currentJob()}, currentThread=${Thread.currentThread()}")
        }
        //不过请大家注意，由于这个线程池是我们自己创建的，因此我们需要在合适的时候关闭它，不然的话：
        myDispatcher.close()

    }

    private fun test4() {

        lifecycleScope.launch(MyContinuationInterceptor()) {
            LjyLogUtil.d("1")
            val job = async {
                delay(1000)
                LjyLogUtil.d("2")
                "hello"
            }
            LjyLogUtil.d("3")
            val result = job.await()
            LjyLogUtil.d("4")
            LjyLogUtil.d("result:$result")
        }
        LjyLogUtil.d("5")
    }

    class MyContinuationInterceptor : ContinuationInterceptor {
        override val key: CoroutineContext.Key<*> = ContinuationInterceptor
        override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
            MyContinuation(continuation)
    }

    class MyContinuation<T>(private val continuation: Continuation<T>) :
        Continuation<T> {
        override val context: CoroutineContext = continuation.context

        /**
         * 所有协程启动的时候，都会有一次 Continuation.resumeWith 的操作，
         * 这一次操作对于调度器来说就是一次调度的机会，我们的协程有机会调度到其他线程的关键之处就在于此
         */
        override fun resumeWith(result: Result<T>) {
            LjyLogUtil.d("MyContinuation: $result")
            continuation.resumeWith(result)
        }
    }

    /**
     * 仿照 Thread.currentThread() 来一个获取当前 Job 的方法
     */
    private suspend fun Job.Key.currentJob() = coroutineContext[Job]

    private fun test3() {
        lifecycleScope.launch {
            LjyLogUtil.d("currentJob=${Job.currentJob()}, currentThread=${Thread.currentThread()}")
        }
        lifecycleScope.launch(context = EmptyCoroutineContext) {
            LjyLogUtil.d("currentJob=${Job.currentJob()}, currentThread=${Thread.currentThread()}")
        }

        lifecycleScope.launch(context = CoroutineName("Hello")) {
            LjyLogUtil.d("currentJob=${Job.currentJob()}, currentThread=${Thread.currentThread()}")
        }
    }

    /**
     * 合并多个请求结果，并发执行多个任务，节约请求耗时
     */
    private fun test2() {
        lifecycleScope.launch(Dispatchers.Main) {
            LjyLogUtil.d("test2 start：${Thread.currentThread().name}_${System.currentTimeMillis() - startTime}")
            // 1.通过网络获取用户信息
            val userInfo = async { getUserInfo(1001) }
            // 2.通过网络加载用户头像
            val img =
                async { getUserPhoto("https://pic3.zhimg.com/v2-dc32dcddfd7e78e56cc4b6f689a24979_xl.jpg") }
            showUserInfo(userInfo.await())
            showUserPhoto(img.await())
        }
    }

    /**
     * 多个任务链时减少回调地域
     */
    private fun test1() {
        lifecycleScope.launch(Dispatchers.Main) {
            LjyLogUtil.d("test1 start: ${Thread.currentThread().name}_${System.currentTimeMillis() - startTime}")
            // 1.通过网络获取用户信息
            val userInfo = getUserInfo(1001)
            // 2.展示用户信息
            showUserInfo(userInfo)
            // 3.通过网络加载用户头像
            val img =
                getUserPhoto(userInfo.photoUrl)
            // 4.展示用户头像
            showUserPhoto(img)
        }
    }

    private suspend fun getUserInfo(userId: Int): UserInfo = withContext(Dispatchers.IO) {
        delay(4000)//模拟网络请求耗时
        LjyLogUtil.d("getUserInfo_${Thread.currentThread().name}_${System.currentTimeMillis() - startTime}")
        return@withContext UserInfo(
            userId,
            "JinYang",
            "https://pic3.zhimg.com/v2-dc32dcddfd7e78e56cc4b6f689a24979_xl.jpg",
            "一个Android开发，公众号「今阳说」"
        )
    }

    private fun showUserInfo(userInfo: UserInfo) {
        tv_info.text = "${userInfo.id}_${userInfo.name}_$${userInfo.description}"
        LjyLogUtil.d("showUserInfo: ${Thread.currentThread().name}_${System.currentTimeMillis() - startTime}")
    }

    private suspend fun getUserPhoto(url: String): Drawable = withContext(Dispatchers.IO) {
        delay(2000)//模拟网络请求图片耗时
        LjyLogUtil.d("getUserPhoto: ${Thread.currentThread().name}_${System.currentTimeMillis() - startTime}")
        return@withContext Glide.with(this@CoroutineActivity).load(url).submit().get()
    }

    private fun showUserPhoto(img: Drawable) {
        iv_photo.setImageDrawable(img)
        LjyLogUtil.d("showUserPhoto: ${Thread.currentThread().name}_${System.currentTimeMillis() - startTime}")
    }


    data class UserInfo(
        val id: Int,
        val name: String,
        val photoUrl: String,
        val description: String
    )

}

